/*****************************************************************************
 * Copyright (c) 2010 Atos Origin.
 *
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *   Atos Origin - Initial API and implementation
 *
 *****************************************************************************/
package org.eclipse.papyrus.uml.diagram.common.groups.core;

import java.util.HashMap;
import java.util.Map;

import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;
import org.eclipse.papyrus.infra.widgets.toolbox.notification.INotification;
import org.eclipse.papyrus.uml.diagram.common.groups.core.ui.ChooseChildrenNotificationConfigurator;
import org.eclipse.papyrus.uml.diagram.common.groups.core.ui.ChooseParentNotificationConfigurator;
import org.eclipse.papyrus.uml.diagram.common.groups.core.ui.NotificationConfigurator;

/**
 * The pending group notifications manager handles notifications which have not been resolved.
 * It ensures that notifications are reconducted in the next notification and that old notifications are deleted.
 *
 * @author vhemery
 */
public class PendingGroupNotificationsManager {

	/** the instances */
	private static Map<DiagramEditPart, PendingGroupNotificationsManager> instances = new HashMap<DiagramEditPart, PendingGroupNotificationsManager>();

	private DiagramEditPart diagramEditPart = null;

	private Map<IGraphicalEditPart, NotificationConfigurator> chooseParentNotifications = new HashMap<IGraphicalEditPart, NotificationConfigurator>();

	private HashMap<IGraphicalEditPart, NotificationConfigurator> chooseChildrenNotifications = new HashMap<IGraphicalEditPart, NotificationConfigurator>();

	private PendingGroupNotificationsManager(DiagramEditPart diagramPart) {
		diagramEditPart = diagramPart;
	}

	/**
	 * Delete this manager and its external references.
	 */
	public void delete() {
		chooseParentNotifications.clear();
		chooseChildrenNotifications.clear();
		instances.remove(diagramEditPart);
		diagramEditPart = null;

	}

	/**
	 * Access to an existing instance or create a new one for an active diagram
	 *
	 * @param diagramPart
	 *            the active diagram part
	 * @return the PendingGroupNotificationsManager instance for the active diagram or null if the diagram is unactive
	 */
	public static PendingGroupNotificationsManager getInstanceForDiagram(DiagramEditPart diagramPart) {
		if (diagramPart.isActive()) {
			if (!instances.containsKey(diagramPart)) {
				PendingGroupNotificationsManager instance = new PendingGroupNotificationsManager(diagramPart);
				instances.put(diagramPart, instance);
				return instance;
			} else {
				return instances.get(diagramPart);
			}
		} else {
			return null;
		}
	}

	/**
	 * Delete the PendingGroupNotificationsManager instance corresponding to this diagram part
	 *
	 * @param diagramPart
	 *            the diagram edit part
	 */
	public static void removeInstanceForDiagram(DiagramEditPart diagramPart) {
		if (instances.containsKey(diagramPart)) {
			instances.get(diagramPart).delete();
		}
	}

	/**
	 * Update the map and delete the no longer needed notifications.
	 * The old notification entries are added to the map, unneccessary entries are removed.
	 *
	 * @param movedPartsAndParents
	 *            the map with moved parts and their parent groups
	 */
	// public void updateParentAssignementsMap(Map<IGraphicalEditPart, List<IGraphicalEditPart>> movedPartsAndParents) {
	// Set<IGraphicalEditPart> newlyMovedParts = movedPartsAndParents.keySet();
	// if(newlyMovedParts.size() > 0) {
	// // the parent assignment from the same diagram must be combined
	// // 1. report unchanged assignments in the new map
	// Map<INotification, ChooseContainingGroupCreator> copy = new HashMap<INotification, ChooseContainingGroupCreator>(chooseParentNotifications);
	// for(Entry<INotification, ChooseContainingGroupCreator> creatorEntry : copy.entrySet()) {
	// if(!creatorEntry.getKey().isDeleted()) {
	// // report
	// ChooseContainingGroupCreator creator = creatorEntry.getValue();
	// Map<IGraphicalEditPart, List<IGraphicalEditPart>> previousAssignement = creator.getElementsAndParents();
	// Set<IGraphicalEditPart> oldlyMovedParts = new HashSet<IGraphicalEditPart>(previousAssignement.keySet());
	// oldlyMovedParts.removeAll(newlyMovedParts);
	// for(IGraphicalEditPart partToReport : oldlyMovedParts) {
	// // do not report orphan parts
	// if(partToReport.getParent() != null) {
	// movedPartsAndParents.put(partToReport, previousAssignement.get(partToReport));
	// }
	// }
	// } else {
	// // remove deleted notification from manager
	// chooseParentNotifications.remove(creatorEntry.getKey());
	// }
	// }
	// // 2. delete old notification
	// removeChooseParentNotification();
	// }
	// // clean useless assignments with only one parent
	// for(Entry<IGraphicalEditPart, List<IGraphicalEditPart>> entry : new HashMap<IGraphicalEditPart, List<IGraphicalEditPart>>(movedPartsAndParents).entrySet()) {
	// if(entry.getValue().size() < 2) {
	// movedPartsAndParents.remove(entry.getKey());
	// }
	// }
	// }

	/**
	 * Remove notifications to assign the graphical parent group
	 */
	public void removeChooseParentNotification(IGraphicalEditPart childEditPart) {
		chooseParentNotifications.remove(childEditPart);
	}

	/**
	 * Remove notification to assign a group's graphical children
	 *
	 * @param parentGroup
	 *            the group
	 */
	public void removeChooseChildrenNotification(IGraphicalEditPart parentGroup) {
		chooseChildrenNotifications.remove(parentGroup);
	}



	/**
	 * Store a notification and its composite creator to let it be managed
	 *
	 * @param creator
	 *            the composite creator
	 * @param notification
	 *            the notification
	 */
	public void storeNotification(NotificationConfigurator configurator) {
		if (configurator instanceof ChooseParentNotificationConfigurator) {
			chooseParentNotifications.put(((ChooseParentNotificationConfigurator) configurator).getMainEditPart(), configurator);
		} else if (configurator instanceof ChooseChildrenNotificationConfigurator) {
			chooseChildrenNotifications.put(((ChooseChildrenNotificationConfigurator) configurator).getMainEditPart(), configurator);
		}
	}

	/**
	 * Get the {@link INotification} for choosing the children of an edit and return null is there none
	 *
	 * @param parentEditPart
	 * @return
	 */
	public NotificationConfigurator getChooseChildrenPendingNotification(IGraphicalEditPart parentEditPart) {
		if (chooseChildrenNotifications.containsKey(parentEditPart)) {
			return chooseChildrenNotifications.get(parentEditPart);
		} else {
			return null;
		}
	}
}
